package com.globopt.global.algorithm;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * This class contains all necessary calculations.
 * 
 * @author Richard Gasztany 2012.11.29.
 * 
 */
public class Global {
	private Function fun;
	private Opts opts;
	private double[] lowerBound;
	private double[] upperBound;

	private LinkedList<Point> xStar;
	private int numberFunctionEvaluation;
	private PointValuePair minimums;

	/**
	 * Simple constructor
	 * 
	 * @param fun
	 * @param opts
	 * @param lowerBound
	 * @param upperBound
	 */
	public Global(Function fun, Opts opts, double[] lowerBound, double[] upperBound) {
		this.fun = fun;
		this.opts = opts;
		opts.getMethod().setGlobal(this);
		this.lowerBound = lowerBound;
		this.upperBound = upperBound;
		this.xStar = new LinkedList<Point>();
		this.numberFunctionEvaluation = 0;
	}

	/**
	 * This method calculate the minimum points.
	 */
	public void calculate() {
		PointValuePair pvp = new PointValuePair(opts.getN(), fun.nvars);
		Point pStar = new Point(fun.nvars);

		int k = 0;

		System.out.println(opts.getRk());

		while (true) {
			pvp = new PointValuePair(opts.getN(), fun.nvars);
			pvp.randomFill(fun, lowerBound, upperBound);
			pvp.sort();
			pvp = pvp.head(opts.getGammak());

			// pvp.print();

			for (int i = 0; i < pvp.getCount(); i++) {
				pStar = opts.getMethod().loc(pvp.getPoints(i));
				numberFunctionEvaluation += opts.getMethod().getFunctionEvaluation();

				addXStar(pStar);
			}
			if (numberFunctionEvaluation > opts.getMaximumFunctionEvaluation()) {
				break;
			}
			if (xStar.size() > opts.getMaximumNumberOfLocalMinimum()) {
				break;
			}
			k++;
			if (k == opts.getK()) {
				break;
			}

		}

		Iterator<Point> it = xStar.iterator();
		System.out.println("min number:" + xStar.size());
		minimums = new PointValuePair(xStar.size(), fun.nvars);
		while (it.hasNext()) {
			Point x = it.next();
			double f = fun.evaluation(x);
			minimums.add(x, f);
		}
		minimums.sort();
		minimums.print();
	}

	public void addXStar(Point p) {
		Iterator<Point> it = xStar.iterator();
		while (it.hasNext()) {
			Point pp = it.next();
			if (pp.distance(p) < opts.getRk()) {
				return;
			}
		}
		xStar.add(p);
	}

	/**
	 * @return the fun
	 */
	public Function getFun() {
		return fun;
	}

	/**
	 * @param i
	 *            index
	 * @return the lowerBound
	 */
	public double getLowerBound(int i) {
		return lowerBound[i];
	}

	/**
	 * 
	 * @param i
	 *            index
	 * @return the upperrBound
	 */
	public double getUpperBound(int i) {
		return upperBound[i];
	}

	/**
	 * @return the opts
	 */
	public Opts getOpts() {
		return opts;
	}

	/**
	 * @return the numbeOfMinimum
	 */
	public int getNumberOfMinimum() {
		return xStar.size();
	}

	public String getResult() {
		return minimums.toString();
	}

}
